package com.autumn.zero.file.storage.services.impl;

import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;

import com.autumn.common.api.application.DataValidation;
import com.autumn.domain.services.AbstractDomainService;
import com.autumn.file.storage.FileObject;
import com.autumn.file.storage.FileStorageObject;
import com.autumn.file.storage.StorageClient;
import com.autumn.mybatis.criterion.EntityQuery;
import com.autumn.runtime.session.IAutumnSession;
import com.autumn.util.AutoMapUtils;
import com.autumn.util.ExceptionUtils;
import com.autumn.zero.file.storage.entities.FileAttachmentInformation;
import com.autumn.zero.file.storage.repositories.IFileAttachmentInformationRepository;
import com.autumn.zero.file.storage.services.IFileAttachmentInformationService;
import com.autumn.zero.file.storage.services.vo.FileAttachmentInformationRequest;
import com.autumn.zero.file.storage.services.vo.FileAttachmentInformationResponse;
import com.autumn.zero.file.storage.services.vo.IUseUploadFileRequest;
import com.autumn.zero.file.storage.services.vo.UseUploadFileRequest;

/**
 * 
 * @author 老码农 2019-03-17 17:43:43
 */
public class FileAttachmentInformationServiceImpl extends AbstractDomainService
		implements IFileAttachmentInformationService {

	@Autowired
	private IAutumnSession session;

	@Autowired
	private StorageClient storageClient;

	@Autowired
	private IFileAttachmentInformationRepository fileAttachmentInformationRepository;

	/**
	 * @throws Exception
	 * 
	 */
	@Override
	public FileAttachmentInformationResponse saveUploadFile(FileAttachmentInformationRequest request) throws Exception {
		ExceptionUtils.checkNotNull(request, "request");
		try {
			FileObject fileObject = storageClient.saveFile(storageClient.getDefaultBucketName(),
					request.getFullUrlPath(), request.getInputStream());
			try {
				FileAttachmentInformation information = new FileAttachmentInformation();
				information.setFileAttachmentType(request.getFileAttachmentType());
				information.setTargetId(null);
				information.setModuleName(request.getModuleName());
				information.setExtensionName(fileObject.getFileInfo().getExtensionName());
				information.setFileFriendlyLength(fileObject.getFileInfo().getLengthString());
				information.setFileFriendlyName(request.getFileFriendlyName());
				information.setFileLength(fileObject.getFileInfo().getLength());
				information.setFileName(fileObject.getFileInfo().getName());
				information.setUrlPath(fileObject.getFileInfo().getPath());
				information.setUrlFullPath(fileObject.getFileInfo().getFullPath());
				information.setUse(false);
				information.setUploadExplain("");
				information.setUploadUserId(this.session.getUserId());
				information.setUploadUserName(this.session.getUserName());
				information.setUploadTime(new Date());
				information.forNullToDefault();
				fileAttachmentInformationRepository.insert(information);
				FileAttachmentInformationResponse response = AutoMapUtils.map(information,
						FileAttachmentInformationResponse.class);
				response.setAccessUrlPath(fileObject.getAccessUrl());
				return response;

			} catch (Exception e) {
				storageClient.deleteFile(storageClient.getDefaultBucketName(), request.getFullUrlPath());
				throw e;
			}
		} finally {
			IOUtils.closeQuietly(request.getInputStream());
		}
	}

	@Override
	public List<FileAttachmentInformationResponse> useUploadFile(Integer fileAttachmentType, Long targetId,
			List<Long> uploadIds) {
		List<UseUploadFileRequest> uploadRequestList = new ArrayList<>();
		if (uploadIds != null) {
			for (Long uploadId : uploadIds) {
				UseUploadFileRequest req = new UseUploadFileRequest();
				req.setUploadId(uploadId);
				req.setIdentification(null);
				req.setUploadExplain("");
				uploadRequestList.add(req);
			}
		}
		return this.useUploadFileByIdentification(fileAttachmentType, targetId, uploadRequestList);
	}

	private <R extends IUseUploadFileRequest> Map<Long, R> getUploadRequestMap(List<R> uploadRequestList) {
		Map<Long, R> map = new HashMap<>();
		if (uploadRequestList != null) {
			for (R useUploadFileRequest : uploadRequestList) {
				if (useUploadFileRequest instanceof DataValidation) {
					DataValidation dataValidation = (DataValidation) useUploadFileRequest;
					dataValidation.valid();
				}
				map.put(useUploadFileRequest.getUploadId(), useUploadFileRequest);
			}
		}
		return map;
	}

	@Override
	public <R extends IUseUploadFileRequest> List<FileAttachmentInformationResponse> useUploadFileByIdentification(
			Integer fileAttachmentType, Long targetId, List<R> uploadRequestList) {
		this.checkCommonInput(fileAttachmentType, targetId);
		Map<Long, R> map = this.getUploadRequestMap(uploadRequestList);
		List<FileAttachmentInformationResponse> responseList = new ArrayList<>();
		if (map.size() == 0) {
			this.deleteByTarget(fileAttachmentType, targetId);
			return responseList;
		}
		List<FileAttachmentInformation> items = this.queryFileList(fileAttachmentType, targetId);
		for (Map.Entry<Long, R> entry : map.entrySet()) {
			FileAttachmentInformation information = fileAttachmentInformationRepository.get(entry.getKey());
			if (information == null || !information.getFileAttachmentType().equals(fileAttachmentType)) {
				ExceptionUtils.throwValidationException("确认使用的上传目标文件id为" + entry.getKey() + "不存在或未上传。");
			}
			if (!information.isUse() || !targetId.equals(information.getTargetId())) {
				information.setTargetId(targetId);
				information.setUse(true);
				information.setIdentification(entry.getValue().getIdentification());
				information.setUploadExplain(entry.getValue().getUploadExplain());
				information.forNullToDefault();
				fileAttachmentInformationRepository.update(information);
			}
			FileAttachmentInformationResponse response = AutoMapUtils.map(information,
					FileAttachmentInformationResponse.class);
			response.setAccessUrlPath(
					storageClient.getAccessUrl(storageClient.getDefaultBucketName(), information.getUrlFullPath()));
			responseList.add(response);
		}
		for (FileAttachmentInformation item : items) {
			if (!map.containsKey(item.getId())) {
				this.deleteByStorage(item, true);
			}
		}
		return responseList;
	}

	private void checkCommonInput(Integer fileAttachmentType, Long targetId) {
		ExceptionUtils.checkNotNull(fileAttachmentType, "fileAttachmentType");
		ExceptionUtils.checkNotNull(targetId, "targetId");
	}

	private EntityQuery<FileAttachmentInformation> createQuery(Integer fileAttachmentType, Long targetId) {
		EntityQuery<FileAttachmentInformation> query = new EntityQuery<>(FileAttachmentInformation.class);
		query.eq(FileAttachmentInformation.FILED_FILE_ATTACHMENT_TYPE, fileAttachmentType)
				.eq(FileAttachmentInformation.FILED_TARGET_ID, targetId).orderBy(FileAttachmentInformation.FILED_ID);
		return query;
	}

	private List<FileAttachmentInformation> queryFileList(Integer fileAttachmentType, Long targetId) {
		EntityQuery<FileAttachmentInformation> query = this.createQuery(fileAttachmentType, targetId);
		return query.selectByList(this.fileAttachmentInformationRepository);
	}

	@Override
	public List<FileAttachmentInformationResponse> queryByTargetList(Integer fileAttachmentType, Long targetId) {
		this.checkCommonInput(fileAttachmentType, targetId);
		List<FileAttachmentInformation> items = this.queryFileList(fileAttachmentType, targetId);
		return AutoMapUtils.mapList(items, FileAttachmentInformationResponse.class, (s, t) -> {
			t.setAccessUrlPath(storageClient.getAccessUrl(storageClient.getDefaultBucketName(), s.getUrlFullPath()));
		});
	}

	@Override
	public void deleteByTarget(Integer fileAttachmentType, Long targetId) {
		this.checkCommonInput(fileAttachmentType, targetId);
		EntityQuery<FileAttachmentInformation> query = this.createQuery(fileAttachmentType, targetId);
		List<FileAttachmentInformation> items = query.selectByList(this.fileAttachmentInformationRepository);
		for (FileAttachmentInformation item : items) {
			this.deleteByStorage(item, false);
		}
		query.deleteByWhere(this.fileAttachmentInformationRepository);
	}

	private void deleteByStorage(FileAttachmentInformation item, boolean isDeleteDb) {
		try {
			storageClient.deleteFile(storageClient.getDefaultBucketName(), item.getUrlFullPath());
		} catch (Exception e) {
			this.getLogger().error("删除文件 " + item.getUrlFullPath() + " 出错:" + e.getMessage());
		}
		if (isDeleteDb) {
			this.fileAttachmentInformationRepository.deleteByPrimaryKey(item.getId());
		}
	}

	@Override
	public FileStorageObject getFile(long id) {
		FileAttachmentInformation info = this.fileAttachmentInformationRepository.get(id);
		if (info == null) {
			return null;
		}
		return storageClient.getFile(storageClient.getDefaultBucketName(), info.getUrlFullPath());
	}

}
